<?php

/**
 * Ignus Framework
 *
 * @author Sergey Palyanov <sergey.palyanov@gmail.com>
 * @link http://code.google.com/p/ignus-php/
 * @license http://apache.org/licenses/LICENSE-2.0
 * @package Ignus
 * @subpackage Test
 * @since 0.1
 */

namespace Ignus\Test;

use Ignus\Test\Report\Reporter;

/**
 * @package Ignus
 * @subpackage Test
 * @since 0.1
 */
class Performer
{
    /**
     * @var string Directory with test cases
     */
    private $dir;

    /**
     * @var Reporter Reporter object
     */
    private $reporter;

    /**
     * Directory of test files. Performer will execute all
     * found test files by recursive directory traverse.
     * @param string $dir Path to test files directory
     * @param Reporter $reporter Test reporter class instance
     */
    public function __construct($dir, Reporter $reporter)
    {
        $this->dir = $dir;
        $this->reporter = $reporter;
    }

    /**
     * Executes unit tests within specified directory
     */
    public function run()
    {
        $this->loadTestFiles();
        $classes = get_declared_classes();
        foreach ($classes as $class) {
            if (is_subclass_of($class, 'Ignus\Test\TestCase')) {
                $this->runTestCase($class);
            }
        }
    }

    /**
     * Performs tests on unit test case
     * @param string $class
     */
    private function runTestCase($class)
    {
        $reflector = new \ReflectionClass($class);
        $this->reporter->printHeader($reflector->getShortName());
        $methods = $reflector->getMethods(\ReflectionMethod::IS_PUBLIC);

        /** @var \Ignus\Test\TestCase $testCase */
        $testCase = new $class();

        foreach ($methods as $method) {
            /** @var \ReflectionMethod $method  */
            $testName = $method->getName();
            if (substr($testName, 0, 4) != "test")
                continue;

            try {
                $testCase->setUp();
                $method->invoke($testCase);
                $testCase->tearDown();
                $this->reporter->printTestSuccess($testName);
            }
            catch (AssertionException $e) {
                $this->reporter->printTestFailure($testName, $e);
            }
            catch (\Exception $e) {
                $this->reporter->printUnexpectedException($testName, $e);
            }
        }
        $this->reporter->printFooter();
    }

    /**
     * Loads test files from specified directory
     */
    private function loadTestFiles()
    {
        if (!is_dir($this->dir))
            throw new \RuntimeException("Tests directory '$this->dir' doesn't exists");

        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->dir));
        foreach ($files as $file) {
            /** @var \DirectoryIterator $file */
            if ($file->isDir())
                continue;

            if (substr($file->getBasename(), -4) != '.php')
                continue;

            require_once $file->getRealPath();
        }
    }
}
